home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-09-28 | 21.1 KB | 742 lines | [TEXT/CWIE] |
- /*
- File: EnetDrvrSrc.c
-
- Contains: A sample Ethernet driver source sample based on the sample loopback driver
- which is supplied as part of the OT Module Driver SDK. This source file
- extends the loopback driver to demonstrate
- 1. the installation of the interrupt handler,
- 2. how to access the kAAPLDeviceLogicalAddress property in order to obtain
- the base address registers for the PC Card.
- 3. how to read the Ethernet burned in address (BIA) from either
- attribute memory, if not in the CIS, or from the CISTPL_FUNCE if it is
- in the CIS.
-
- Original comments:
- * Simple loopback driver utilizing Mentat DLPI Template Code (dlpiether.c)
- * This file, combined with dlpiether.c (or linked against the Shared LIbrary
- * containing dlpiether.c) is a functioning loopback driver for Open Transport.
- * This file also serves as a template for writing the hardware-specific
- * code for a driver which will use dlpiether.c. Comments in the code and
- * the accompanying documentation, Mentat DLPI Driver Template, provide
- * necessary information.
-
- Written by: Mentat Inc.
-
- Change History (most recent first):
- 8/16/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
- char sccs_loop_id[] = "@(#)EnetDrvrSrc.c 1.2";
-
- /*
- * "BoardX:" in comments refers to a typical hardware driver. These are hints
- * and suggestions for converting this code to an actual hardware driver.
- */
- #include <OpenTptModule.h>
- #include <dlpi.h>
- #include "Devices.h"
- #include <NameRegistry.h>
- #include <DriverServices.h>
- #include <Interrupts.h>
- #include <OpenTptLinks.h>
- #include <OpenTptPCISupport.h>
- #include "dlpiuser.h"
- #include "dlpiether.h"
- #define ulong UInt32
- #define uint UInt16
-
- #include <PCCard.h>
- #include <PCCardTuples.h>
-
- // enter the name of the module as registered using the OTRegisterPort call.
- // Use the PCCardDisplayNameRegistry utility to find the module name for the
- // This name MUST match else TCP/IP services WILL not work.
- #define kEnetCardName "myEnetDriverName"
-
- /*
- * The following defines are inserted here for convenience for the
- * loopback driver. A "real" driver may place these in separate header
- * files and/or use system-defined values.
- */
- //#define LOOPBACK_DRIVER /* conditionally compiles loopback code */
-
- #define ENETADDRINATTRIBUTE /* conditionally compiles code to obtain the ethernet
- burned in address from attribute memory
- comment out this define to had code access BIA
- from CIS
- */
-
- /* Some useful MacBug debugging macros */
-
- #define mps_printf OTKernelPrintfForMentat
- #define loop_error(a) mps_printf a
- #define loop_trace(a) mps_printf a
- #define loop_debug(a) if(loop_debug) {mps_printf a;} else {;}
- #define loop_debug2(a) if(loop_debug >= 2) {mps_printf a;} else {;}
- #define trace_args " "
- #define trace0 "0 " /* Fatal */
- #define trace1 "1 " /* General */
- #define trace2 "2 " /* Out of Memory */
- #define trace3 "3 " /* Event */
- #define trace_eol
- extern int OTKernelPrintfForMentat(char * fmt, ...);
- int loop_debug = 1;
-
- #define MAX_PACKET_SIZE 1500 /* We're ethernet, remember :-) */
- #define MIN_PACKET_SIZE 60 /* We'll pad short packets Ethernet style */
- #define LOOP_HIWAT 2048 /* Flow control high water mark */
- #define LOOP_LOWAT 64 /* Flow control low water mark */
-
- /* just so DL_INFO_ACK has something to report */
- static unsigned char my_hardware_addr[] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
-
- /*
- * See OpnTptLinks.h for more
- */
-
- /* Driver information 'per board', e.g., internal control structure. */
- typedef struct board_s {
- dle_t board_dle; /* Must be first in structure. */
- /* BoardX: Fields needed for controlling hardware go here */
- } board_t;
-
- /*
- * STREAMS instance data (q_ptr)
- */
- typedef struct loop_s {
- dcl_t loop_dcl; /* Must be first in structure */
- /* BoardX: Any 'per stream' fields needed by board go here */
- } loop_t;
- #define loop_dle loop_dcl.dcl_hw /* pointer to board_t structure */
-
- static void loop_bcopy_to_noncached(unsigned char * src, unsigned char * dst
- , unsigned int count);
- static int loop_close(queue_t * q, int flag, cred_t * credp);
- static void loop_hw_address_filter_reset(void * loopvp, dle_addr_t * addr_list
- , ulong addr_count, ulong promisc_count
- , ulong multi_promisc_count, ulong accept_broadcast
- , ulong accept_error);
- static void loop_hw_start(void * loopvp);
- static void loop_hw_stop(void * loopvp);
- static int loop_open(queue_t * q, dev_t * devp, int flag, int sflag
- , cred_t * credp);
- static int loop_rsrv(queue_t * q);
- static int loop_wput(queue_t * q, mblk_t * mp);
- install_info* GetOTInstallInfo ();
- Boolean InitStreamModule (RegEntryID * our_id);
- void board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count);
- // DRF start: added for PCCard Kitchen
-
- InterruptMemberNumber board_intr(InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount);
- void TerminateStreamModule (void);
- OTResult ValidateHardware (RegEntryID * our_id);
-
- InterruptEnabler gInterruptEnabler;
- InterruptDisabler gInterruptDisabler;
-
- volatile void * gCardBaseAddress;
- unsigned char gEthernetHardwareAddress[6];
-
- // DRF end
-
-
- /*
- * Global pointer to the board_t structure for this device instantiation. This
- * structure is allocated in the InitStreamModule routine and freed in
- * TerminateStreamModule. CFM guarantees us a separate data space for all
- * instances of the device, so this pointer is always unique.
- */
- board_t * board_global;
-
- /* STREAMS configuration structures. */
- struct module_info info = {
- kEnetModuleID, kEnetCardName
- , MIN_PACKET_SIZE, MAX_PACKET_SIZE
- , LOOP_HIWAT, LOOP_LOWAT
- };
-
- /*
- * STREAMS qinit structures. Drivers have no rput.
- */
- struct qinit rinit = {
- NULL, loop_rsrv, loop_open, loop_close, NULL, &info
- };
-
- struct qinit winit = {
- loop_wput, NULL, loop_open, loop_close, NULL, &info
- };
-
- struct streamtab loopback_info = { &rinit, &winit };
-
- /*
- * BoardX: Hardware drivers would typically add 'kOTModUsesInterrupts' to
- * flags entry of following.
- */
-
- install_info loopback_install_info =
- {
- &loopback_info,
- kOTModIsDriver | kOTModUpperIsDLPI,
- SQLVL_MODULE,
- NULL,
- 0,
- 0
- };
-
- void board_bcopy_to_noncached (unsigned char * src, unsigned char * dst, unsigned int count)
- {
- unsigned int iterations;
-
- /* Do short copies with a simple loop. */
- if (count <= 15) {
- do {
- --count;
- *dst++ = *src++;
- } while (count);
- return;
- }
-
- /* Align on 8 byte dst boundary */
- iterations = (8 - ((unsigned int)dst & 0x7)) & 0x7;
- count -= iterations;
- if (iterations) {
- do {
- --iterations;
- *dst++ = *src++;
- } while (iterations);
- }
-
- /* Copy 32 byte chunks */
- iterations = count >> 5;
- count &= 0x1F;
- if (iterations) {
- double * dsrc = (double *)src;
- double * ddst = (double *)dst;
- do {
- iterations--;
- ddst[0] = dsrc[0];
- ddst[1] = dsrc[1];
- ddst[2] = dsrc[2];
- ddst[3] = dsrc[3];
- ddst += 4;
- dsrc += 4;
- } while (iterations);
- src = (unsigned char *)dsrc;
- dst = (unsigned char *)ddst;
- }
-
- /* Copy 4 byte chunks */
- iterations = count >> 2;
- count &= 0x3;
- if (iterations) {
- unsigned long * lsrc = (unsigned long *)src;
- unsigned long * ldst = (unsigned long *)dst;
- do {
- --iterations;
- *ldst++ = *lsrc++;
- } while (iterations);
- src = (unsigned char *)lsrc;
- dst = (unsigned char *)ldst;
- }
-
- /* Copy the rest */
- while (count--)
- *dst++ = *src++;
- }
-
- /*
- * Template interrupt routine for processing inbound packets.
- */
-
- InterruptMemberNumber board_intr (InterruptSetMember ISTmember, void *refCon, UInt32 theIntCount)
- {
- #pragma unused(ISTmember,refCon,theIntCount)
- board_t * board = board_global;
- dle_t * dle = &board->board_dle;
-
- OTEnterInterrupt();
-
- loop_debug((trace_args "entered" trace_eol));
-
- OTLeaveInterrupt();
- return 1;
- }
-
-
- /* This function must be exported; see loop.exp */
- install_info *
- GetOTInstallInfo ()
- {
- DebugStr("\p entered GetOTInstallInfo ");
- loop_debug((trace_args "GetOTInstallInfo" trace_eol));
- return &loopback_install_info;
- }
-
- Boolean
- InitStreamModule (RegEntryID * our_id)
- {
- board_t * board;
- dle_t * dle;
- // DRF start: added for PCCard Kitchen
- RegPropertyValueSize propertySize;
- InterruptSetMember our_ist;
- InterruptHandler old_interrupt_handler;
- void * refCon;
- OSStatus status;
-
- #ifdef ENETADDRINATTRIBUTE
- // ethernet address is in attribute memory, but not CIS
- LogicalAddress attribute_window_base;
- ByteCount attribute_window_size;
- PCCardAccessSpeed attribute_window_speed;
- PCCardWindowOffset attribute_window_offset;
- PCCardWindowID attribute_window_id;
- #else
- // ethernet address is in CISTPL_FUNCE
- PCCardTupleIterator tupleIterator;
- char tupleBuffer[MAX_TUPLE_SIZE];
- UInt32 tupleBufferSize;
- PCCardTupleKind foundTuple;
- UInt32 foundTupleDataSize;
- #endif
- // DRF end
-
- loop_debug((trace_args "InitStreamModule" trace_eol));
-
- /* Allocate the board_t structure for this device */
- board = (board_t *)OTAllocMem(sizeof(board_t));
- if (!board)
- return false;
-
- /* These are the hardware start/stop/reset functions */
- bzero((char *)board, sizeof(board_t));
- dle = &board->board_dle;
- dle->dle_hw.dlehw_start = loop_hw_start;
- dle->dle_hw.dlehw_stop = loop_hw_stop;
- dle->dle_hw.dlehw_address_filter_reset = loop_hw_address_filter_reset;
- dle->dle_hw.dlehw_send_error = NULL;
- dle->dle_hw.dlehw_recv_error_flags = 0;
-
- /*
- * Suggestions:
- * - Install interrupt vectors in case any memory allocations are
- * required. Interrupts should not be enabled until the
- * board start routine is called.
- * - Reset the hardware to a known state. If the hardware does
- * not respond, then return false.
- * - Read the Ethernet address from the board's ROM area. This
- * address should be copied into both the dle_factory_addr
- * field and the dle_current_addr field.
- */
-
- // DRF start: added for PCCard Kitchen
-
- /* install interrupt handler */
-
- loop_debug((trace_args "about to install interrupt handler!" trace_eol));
-
- propertySize = sizeof(InterruptSetMember);
- status = RegistryPropertyGet(our_id, kISTPropertyName, &our_ist, &propertySize);
- if (status != noErr) {
- loop_debug((trace_args "InitStreamModule install interrupt vector: can't get driver-ist property." trace_eol));
- return status;
- }
-
- status = GetInterruptFunctions(our_ist.setID, our_ist.member,
- &refCon, &old_interrupt_handler, &gInterruptEnabler, &gInterruptDisabler );
- if (status != noErr) {
- loop_debug((trace_args "InitStreamModule install interrupt vector: can't get interrupt functions." trace_eol));
- return status;
- }
-
- status = InstallInterruptFunctions(our_ist.setID, our_ist.member,
- NULL, board_intr, gInterruptEnabler, gInterruptDisabler );
- if (status != noErr) {
- loop_debug((trace_args "InitStreamModule install interrupt vector: can't set interrupt functions." trace_eol));
- return status;
- }
-
- /* get base address */
-
- // DebugStr("\p about to get base address!");
-
- propertySize = sizeof(gCardBaseAddress);
- status = RegistryPropertyGet(our_id, kAAPLDeviceLogicalAddress, &gCardBaseAddress, &propertySize);
- if (status != noErr) {
- loop_debug((trace_args "InitStreamModule install interrupt vector: can't get AAPL,address property." trace_eol));
- return status;
- }
-
-
- // Get Ethernet Hardware Address
-
- #ifdef ENETADDRINATTRIBUTE
- // If your card has ethernet hardware address in attribute memory, but NOT in a tuple, then use the following:
-
- /* IMPORTANT NOTE */
- // the following values are for compilation purposes only. When accessing the ethernet address
- // from attribute memory, the following values must be set as appropriate for your card.
-
- attribute_window_size = 4096;
- attribute_window_speed = 600;
- attribute_window_offset = 0;
- status = PCCardRequestWindow(our_id, kPCCardAttributeMemorySpace, &attribute_window_base, &attribute_window_size,
- &attribute_window_speed, &attribute_window_offset, &attribute_window_id);
- if (status != noErr) {
- loop_debug((trace_args "InitStreamModule get ethernet address: can't request attribute window" trace_eol));
- return status;
- }
-
- status = PCCardReleaseWindow(attribute_window_id);
-
- // save the Ethernet BIA into the dle structure as specified in the DLPI template note
- bcopy(dle->dle_factory_addr, attribute_window_base, 6);
- bcopy(dle->dle_current_addr, attribute_window_base, 6);
-
- #else
- // if your card has ethernet hardware address in CISTPL_FUNCE, it is much easier
-
- tupleIterator = PCCardNewTupleIterator();
- if (tupleIterator == NULL) {
- loop_debug((trace_args "InitStreamModule get ethernet address: can't make tuple iterator" trace_eol));
- return status;
- }
-
- tupleBufferSize = sizeof(tupleBuffer);
- status = PCCardGetFirstTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize,
- &foundTuple, &foundTupleDataSize);
-
- while (status == noErr) {
- // we found a CISTPL_FUNCE tuple
-
- DebugStr("\pFound CISTPL_FUNCE"); // for RATOC card, the address is stored starting at the 5th byte
-
- if (1) {
- my_hardware_addr[0] = tupleBuffer[5];
- my_hardware_addr[1] = tupleBuffer[6];
- my_hardware_addr[2] = tupleBuffer[7];
- my_hardware_addr[3] = tupleBuffer[8];
- my_hardware_addr[4] = tupleBuffer[9];
- my_hardware_addr[5] = tupleBuffer[10];
-
- DebugStr("\pgot hardware address");
-
- break;
- }
-
- tupleBufferSize = sizeof(tupleBuffer);
- status = PCCardGetNextTuple(our_id, CISTPL_FUNCE, tupleIterator, &tupleBuffer[0], &tupleBufferSize,
- &foundTuple, &foundTupleDataSize);
- }
-
- PCCardDisposeTupleIterator(tupleIterator);
-
- // save the Ethernet BIA into the dle structure as specified in the DLPI template note
- bcopy(dle->dle_factory_addr, my_hardware_addr, 6);
- bcopy(dle->dle_current_addr, my_hardware_addr, 6);
-
- #endif
-
- // DRF end
-
-
- /*
- * Allow the DLPI common code to initialize the dle fields. Once
- * dle_init is called, dle_terminate must be called before freeing
- * the dle structure. There is private memory allocated for each
- * dle that needs to be freed.
- */
- dle_init(dle, 0);
- board_global = board;
-
- return true;
- }
-
- void
- TerminateStreamModule (void)
- {
- board_t * board = board_global;
-
- loop_debug((trace_args "TerminateStreamModule" trace_eol));
-
- /*
- * Suggestions:
- * - Remove interrupt vectors.
- * - Reset the hardware to a known state.
- */
-
- board_global = (board_t *)NULL;
- dle_terminate(&board->board_dle);
- OTFreeMem(board);
- }
-
- OTResult
- ValidateHardware (RegEntryID * our_id)
- {
- #pragma unused(our_id)
- loop_debug((trace_args "ValidateHardware" trace_eol));
- return kOTNoError;
- }
-
- /* STREAMS close routine. */
- int loop_close (queue_t * q, int flag, cred_t * credp)
- {
- #pragma unused(flag,credp)
- loop_debug((trace_args "loop_close(%x), dcl_dle %x" trace_eol
- , q, ((dcl_t *)q->q_ptr)->dcl_hw ));
- /*
- * NOTE: there is probably not much else that needs to be done
- * in this routine. If you need to know about the number of
- * open instances, dle_refcount is the number of streams still
- * referencing the device (decremented in dle_close).
- */
- return dle_close(q);
- }
-
- void loop_hw_address_filter_reset (void * boardvp, dle_addr_t * addr_list
- , ulong addr_count, ulong promisc_count
- , ulong multi_promisc_count, ulong accept_broadcast
- , ulong accept_error)
- {
- #pragma unused(addr_count,accept_broadcast,accept_error)
- board_t * board = boardvp;
- dle_t * dle = &board->board_dle;
- dle_addr_t * dlea;
- unsigned char * first_phys_addr = 0;
- uint phys_addr_count = 0;
-
- /* Calculate the new logical address filter for multicast addresses. */
- for (dlea = addr_list; dlea; dlea = dlea->dlea_next) {
- if (dlea->dlea_addr[0] & 0x1) {
- /*
- * If the address is a multicast address, then set
- * the right bits in the new filter.
- */
- } else {
- /*
- * Additional physical address. This does not happen
- * with the first version of the DLPI template code.
- * However, at some future time, we may want to
- * support multiple physical addresses on one
- * hardware tap.
- */
- if (!first_phys_addr)
- first_phys_addr = dlea->dlea_addr;
- phys_addr_count++;
- }
- }
-
- if (first_phys_addr) {
- /*
- * If there were any physical, non-multicast addresses in
- * the list, then check to see if the first one is different
- * from the current one. If so, copy the new address into
- * dle_current_addr and take whatever steps are necessary
- * with the hardware to change the physical address.
- */
- ;
- }
-
- /*
- * Compare the new address filter with the old one. If the new
- * one is different, then set the hardware appropriately.
- */
-
- /*
- * If there are multiple physical addresses, then the board
- * probably needs to be put in a promiscuous state.
- */
- if (phys_addr_count > 1)
- promisc_count |= 1;
-
- if (promisc_count || multi_promisc_count)
- /* Set the board into promiscuous mode. */;
- else
- /* Clear any promiscuous mode that may be set*/;
- /*
- * Save the promiscuous setting in the board structure so that
- * updates to the hardware registers from loop_start or other
- * routines will be correct.
- */
- }
-
- /* Called by dlpiether code through dlehw_start. */
- void loop_hw_start (void * boardvp)
- {
- board_t * board = boardvp;
-
- /* Kick the board alive and allow receive interrupts. */
- }
-
- /* Called by dlpiether code through dlehw_stop. */
- void loop_hw_stop (void * boardvp)
- {
- #pragma unused(boardvp)
- /* Turn off interrupts and leave the hardware disabled. */
- }
-
-
- /* STREAMS open routine. */
- int loop_open (queue_t * q, dev_t * devp, int flag, int sflag, cred_t * credp)
- {
- int ret_code;
-
- loop_debug((trace_args "loop_open()" trace_eol));
- /*
- * This routine probably does not need to do anything other
- * than call dle_open. dle_refcnt is incremented.
- */
- ret_code = dle_open(&board_global->board_dle, q, devp, flag, sflag, credp
- , sizeof(loop_t));
- loop_debug((trace_args "dle_open(%x) = %d, dcl_hw %x" trace_eol
- , &board_global->board_dle
- , ret_code
- , ((dcl_t *)q->q_ptr)->dcl_hw ));
- return ret_code;
- }
-
-
- /*
- * Read-side service routine.
- * Pass all inbound packets upstream. Messages are placed on
- * the read-side queue by dle_inbound. Unless the hardware
- * requires something special, no additional code should be
- * required. NOTE: Additional messages may be added to the
- * queue while this routine is running. If canputnext() fails,
- * messages are freed rather than put back on the queue.
- * This is necessary since dle_inbound() will continue to add
- * messages without checking flow control (it can't call canputnext()
- * from interrupt context).
- */
- int loop_rsrv (q)
- queue_t * q;
- {
- mblk_t * mp;
-
- while (mp = getq(q)) {
- /*
- * Private message to be passed back to the dlpiether
- * code. This interface is required for supporting
- * 802.2 XID and Test packets.
- */
- if (mp->b_datap->db_type == M_CTL) {
- dle_rsrv_ctl(q, mp);
- } else if (canputnext(q))
- putnext(q, mp);
- else {
- freemsg(mp);
- flushq(q, FLUSHDATA);
- break;
- }
- }
- return 0;
- }
-
- /* Write-side put routine. Only handles M_DATA, handing others to dlpiether. */
- int
- loop_wput (queue_t * q, mblk_t * mp)
- {
- loop_t * loop;
- mblk_t * first_mp;
- long remaining;
- long total;
- unsigned char * xmt_buf=nil;
-
- loop = (loop_t *)q->q_ptr;
- if (mp->b_datap->db_type != M_DATA) {
- mp = dle_wput(q, mp);
- if (!mp)
- return 0;
- switch (mp->b_datap->db_type) {
- case M_DATA:
- break; /* it's ready to send */
- case M_IOCNAK:
- /*
- * Any driver private ioctl's come back from
- * dle_wput() as an M_IOCNAK with ioc_error
- * set to EINVAL; the rest of the original M_IOCTL
- * is intact (including ioc_cmd & trailing M_DATAs).
- * It may be processed here.
- */
- qreply(q,mp);
- return 0;
- default:
- /* dle_wput() has formatted the reply for us */
- qreply(q, mp);
- return 0;
- }
- }
-
- /*
- * Copy the packet to transmit buffer. This is an example
- * showing the copies and the calculation of the length
- * of the packet. Code for actual hardware devices will
- * obviously need to be updated, at least to initialize
- * xmt_buf to point to the hardware transmit area.
- */
- remaining = MAX_PACKET_SIZE;
- first_mp = mp;
-
- do {
- unsigned char * rptr = mp->b_rptr;
- int len = mp->b_wptr - rptr;
- if (len <= 0)
- continue;
- if (remaining < len) {
- /* packet too large */
- mp = dle_wput_ud_error(first_mp, DL_UNDELIVERABLE, 0);
- if (mp)
- qreply(q, mp);
- return 0;
- }
- remaining -= len;
- xmt_buf += len;
- board_bcopy_to_noncached(rptr, xmt_buf - len, len);
- } while ((mp = mp->b_cont) && remaining);
-
- total = MAX_PACKET_SIZE - remaining;
-
- /* Fill in the 802 length field if it is zero. */
- {
- unsigned char * up;
- up = &xmt_buf[-total];
- if (!(up[12] | up[13]))
- {
- uint adjusted_total = total - 14;
- up[12] = (unsigned char)(adjusted_total >> 8);
- up[13] = (unsigned char)(adjusted_total & 0xff);
- }
- }
-
- if (total < MIN_PACKET_SIZE)
- {
- /*
- * If the packet is less than the minimum transmit
- * size, then you need to pad the packet. Hopefully
- * this is just of matter of setting the hardware
- * to pad automatically.
- */
- ;
- }
-
- /* Trigger the hardware transmit. */
- /* Increment the appropriate MIB interface statistics. */
- ((dle_t *)loop->loop_dle)->dle_istatus.bytes_sent += total;
- if (first_mp->b_rptr[0] & 0x1) {
- unsigned char * rptr = first_mp->b_rptr;
- if ((rptr[0] & rptr[1] & rptr[2] & rptr[3] & rptr[4]
- & rptr[5]) == 0xFF)
- ((dle_t *)loop->loop_dle)->dle_istatus.broadcast_frames_sent++;
- else
- ((dle_t *)loop->loop_dle)->dle_istatus.multicast_frames_sent++;
- } else
- ((dle_t *)loop->loop_dle)->dle_istatus.unicast_frames_sent++;
-
- freemsg(first_mp);
- return 0;
- }
-
-
-
-